﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Speech.Recognition;
using System.Speech.Synthesis;
using System.Threading;
using System.Windows.Forms;
using SongsDB;
using System.Text.RegularExpressions;

namespace MMVoiceComm
{
    internal class VoiceCommander
    {
        #region CommandType enum

        public enum CommandType
        {
            Play = 0,
            PlaySong,
            PlayArtist,
            PlayGenre,
            PlayAlbum,
            Pause,
            Test,
            Unpause,
            Next,
            Previous,
            Stop,
            TogglePause
        }

        #endregion

        private SpeechRecognitionEngine recognitionEngine;
        private SpeechRecognitionEngine recognitionEngineSS;
        private SpeechSynthesizer speaker;

        //Lists that store a copy of the database information we want, for a future feature
        public List<string> albumsList = new List<string>();
        public List<string> artistsList = new List<string>();
        public List<string> genresList = new List<string>();
        public List<string> playlistList = new List<string>();

        //Holds the speech commands, this will allow for localization later
        //The List<string> ais a messy way for there to be multiple ways to say a command

        // 80% confident what we said was what the computer interpreted
        private float minimumConfidence = 0.8f;
        public Boolean startStopEnabled = true;
        public List<string> songsList = new List<string>();
        private Dictionary<string, List<string>> speechCommands = new Dictionary<string, List<string>>();

        public Boolean synthRepeatCommand = false;
        public Boolean autoAddSongs = false;


        private SelectionForm songSelection;
        //easy to see command types, use CamelCase as PlaySong is converted into play song

        private string[] numbers = new string[0];

        private Grammar jumpToSongGrammar;

        //Makes the recognition engine
        //And adds event that listens for it
        public VoiceCommander()
        {
            recognitionEngine = new SpeechRecognitionEngine();
            

            if (startStopEnabled)
            {
                recognitionEngineSS = new SpeechRecognitionEngine();
                
            }
            speaker = new SpeechSynthesizer();
            speaker.SetOutputToDefaultAudioDevice();
        }

        public void SetupRecognition()
        {
            Logger.log("Recognition Started");
            recognitionEngine.SetInputToDefaultAudioDevice();
            recognitionEngine.SpeechRecognized +=
                new EventHandler<SpeechRecognizedEventArgs>(recognitionEngine_SpeechRecognized);
            if (startStopEnabled)
            {
                recognitionEngineSS.SetInputToDefaultAudioDevice();

                recognitionEngineSS.SpeechRecognized +=
                    new EventHandler<SpeechRecognizedEventArgs>(recognitionEngineSS_SpeechRecognized);
            }
        }

        private void recognitionEngineSS_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            try
            {
                if (e.Result.Confidence >= minimumConfidence)
                {
                    if (synthRepeatCommand && speaker != null) speaker.SpeakAsync(e.Result.Text);
                    if (CheckForEquals("start listening", e.Result.Text).Length > 0) HandleStartListening();
                    if (CheckForEquals("stop listening", e.Result.Text).Length > 0) HandleStopListening();
                }
            }
            catch (Exception ex)
            {
            }
        }


        public Dictionary<string, List<string>> SpeechCommands
        {
            set { speechCommands = value; }
        }

        public Boolean SetVoice(string voiceName)
        {
            if (speaker != null)
            {
                Boolean hasVoice = false;
                foreach (InstalledVoice iv in speaker.GetInstalledVoices())
                {
                    if (iv.VoiceInfo.Name.Equals(voiceName))
                    {
                        hasVoice = true;
                        speaker.SelectVoice(voiceName);
                    }
                }
                return hasVoice;
            }
            else
            {
                return false;
            }
        }


        private void recognitionEngine_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            try
            {
                if (e.Result.Confidence >= minimumConfidence)
                {
                    if (genresList != null && speechCommands != null)
                    {//Gotta hope this will catch most of the problems with objects being set to null
                        //Speak the command?
                        if (synthRepeatCommand && speaker != null) speaker.SpeakAsync(e.Result.Text);
                        //Check that it matches one of the commands, then run command if match
                        if (CheckForEquals("test", e.Result.Text).Length > 0) HandleTest();
                        if (CheckForEquals("pause", e.Result.Text).Length > 0) HandlePause();
                        if (CheckForEquals("unpause", e.Result.Text).Length > 0) HandleUnpause();
                        if (CheckForEquals("stop", e.Result.Text).Length > 0) HandleStop();
                        if (CheckForEquals("next", e.Result.Text).Length > 0) HandleNext();
                        if (CheckForEquals("previous", e.Result.Text).Length > 0) HandlePrevious();
                        if (CheckForEquals("play", e.Result.Text).Length > 0) HandlePlay();
                        if (CheckForEquals("toggle pause", e.Result.Text).Length > 0) HandleTogglePause();

                        if (CheckForEquals("jump to start", e.Result.Text).Length > 0) HandleJumpToStart();
                        if (CheckForEquals("jump to end", e.Result.Text).Length > 0) HandleJumpToEnd();



                        if (CheckForStartingWith("jump forward", e.Result.Text).Length > 0)
                            HandleJumpForward(e.Result.Text);
                        if (CheckForStartingWith("jump backward", e.Result.Text).Length > 0)
                            HandleJumpBackward(e.Result.Text);
                        if (CheckForStartingWith("jump to song", e.Result.Text).Length > 0)
                            HandleJumpToSong(e.Result.Text);

                        HandleAddSongToNP(e.Result.Text);
                        HandleAddGenreToNP(e.Result.Text);
                        HandleAddArtistToNP(e.Result.Text);
                        HandleAddAlbumToNP(e.Result.Text);

                        if (CheckForStartingWith("play song", e.Result.Text).Length > 0) HandlePlaySong(e.Result.Text);
                        if (CheckForStartingWith("play playlist", e.Result.Text).Length > 0)
                            HandlePlayPlaylist(e.Result.Text);
                        if (CheckForStartingWith("play artist", e.Result.Text).Length > 0)
                            HandlePlayArtist(e.Result.Text);
                        if (CheckForStartingWith("play genre", e.Result.Text).Length > 0)
                            HandlePlayGenre(e.Result.Text);
                        if (CheckForStartingWith("play album", e.Result.Text).Length > 0)
                            HandlePlayAlbum(e.Result.Text);
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.log(
                    "MMVoiceComm almost killed MediaMonkey because it didn't know what it was doing.\r\n\n" + ex.Message +
                    "\r\n" + ex.StackTrace);
            }
        }

        

        private void HandleAddAlbumToNP(string text)
        {
            //Fairly hacky uses searching for words that are not in the original commands ot try ot find new words that we assume are the song name
            if (speechCommands.ContainsKey("add album to now playing"))
            {
                var matches = new List<string>();
                foreach (string commandValue in speechCommands["add album to now playing"])
                {
                    int songStart = commandValue.IndexOf("{album}");
                    string textAfter = commandValue.Substring(commandValue.IndexOf("{album}") + 6);
                    int songEnd = text.IndexOf(textAfter);

                    if (songStart >= 0 && songEnd >= 0)
                    {
                        //Safe to assume it worked
                        addSongsToPlaylist(
                (SDBSongIterator)
                MMStart.SDB.Database.QuerySongs("Album=\"" + text.Substring(songStart, songEnd - songStart) + "\""), false, false);
                    }
                    else
                    {
                        //MessageBox.Show("Shock the monkey!"+songStart+":"+songEnd+": \""+textAfter+"\"");

                    }


                }
            }
        }

        private void HandleAddArtistToNP(string text)
        {
            //Fairly hacky uses searching for words that are not in the original commands ot try ot find new words that we assume are the song name
            if (speechCommands.ContainsKey("add artist to now playing"))
            {
                var matches = new List<string>();
                foreach (string commandValue in speechCommands["add artist to now playing"])
                {
                    int songStart = commandValue.IndexOf("{artist}");
                    string textAfter = commandValue.Substring(commandValue.IndexOf("{artist}") + 6);
                    int songEnd = text.IndexOf(textAfter);

                    if (songStart >= 0 && songEnd >= 0)
                    {
                        //Safe to assume it worked
                        addSongsToPlaylist(
                (SDBSongIterator)
                MMStart.SDB.Database.QuerySongs("Artist=\"" + text.Substring(songStart, songEnd - songStart) + "\""), false, false);
                    }
                    else
                    {
                        //MessageBox.Show("Shock the monkey!"+songStart+":"+songEnd+": \""+textAfter+"\"");

                    }


                }
            }
        }

        private void HandleAddGenreToNP(string text)
        {
            //Fairly hacky uses searching for words that are not in the original commands ot try ot find new words that we assume are the song name
            if (speechCommands.ContainsKey("add genre to now playing"))
            {
                var matches = new List<string>();
                foreach (string commandValue in speechCommands["add genre to now playing"])
                {
                    int songStart = commandValue.IndexOf("{genre}");
                    string textAfter = commandValue.Substring(commandValue.IndexOf("{genre}") + 6);
                    int songEnd = text.IndexOf(textAfter);

                    if (songStart >= 0 && songEnd >= 0)
                    {
                        //Safe to assume it worked
                        addSongsToPlaylist(
                (SDBSongIterator)
                MMStart.SDB.Database.QuerySongs("Genre=\"" + text.Substring(songStart, songEnd - songStart) + "\""), false, false);
                    }
                    else
                    {
                        //MessageBox.Show("Shock the monkey!"+songStart+":"+songEnd+": \""+textAfter+"\"");

                    }


                }
            }
        }

        private void HandleAddSongToNP(string speechText)
        {//Fairly hacky uses searching for words that are not in the original commands ot try ot find new words that we assume are the song name
            if (speechCommands.ContainsKey("add song to now playing"))
            {
                var matches = new List<string>();
                foreach (string commandValue in speechCommands["add song to now playing"])
                {
                    int songStart = commandValue.IndexOf("{song}");
                    string textAfter = commandValue.Substring(commandValue.IndexOf("{song}") + 6);
                    int songEnd = speechText.IndexOf(textAfter);

                    if (songStart >= 0 && songEnd >= 0)
                    {
                        //Safe to assume it worked
                        addSongsToPlaylist(
                (SDBSongIterator)
                MMStart.SDB.Database.QuerySongs("SongTitle=\"" + speechText.Substring(songStart, songEnd - songStart) + "\""), false, false);
                    }
                    else
                    {
                        //MessageBox.Show("Shock the monkey!"+songStart+":"+songEnd+": \""+textAfter+"\"");

                    }


                }
            }

        }


        private void HandlePlayPlaylist(string text)
        {
            string[] matchingCommands = CheckForStartingWith("play playlist", text);
            //Longest command matched is probably the best one
            string cmd = getLongestString(matchingCommands);

            if (cmd.Length > 0)
            {
                SDBPlaylist songList = MMStart.SDB.get_PlaylistByTitle(text.Substring(cmd.Length + 1));
                if (songList.ChildPlaylists.Count != MMStart.SDB.get_PlaylistByTitle("").ChildPlaylists.Count)
                {
                    //Probably not a root playlist
                    MMStart.SDB.Player.PlaylistClear();
                    MMStart.SDB.Player.PlaylistAddTracks(songList.Tracks);
                    MMStart.SDB.Player.Stop();
                    MMStart.SDB.Player.Play();
                }
            }
        }

        private void HandleStopListening()
        {
            this.StopListening();
        }

        private void HandleStartListening()
        {
            this.StartListening();
            //Set our indicator button to show we are listening
            MMStart.activeButton.Visible = false;
            MMStart.activeButton.IconIndex = 51;
            MMStart.activeButton.Caption = "Listening";
            MMStart.activeButton.Visible = true;
        }

        private void HandleJumpToSong(string text)
        {

            string[] matchingCommands = CheckForStartingWith("jump to song", text);
            //Longest command matched is probably the best one
            string cmd = getLongestString(matchingCommands);
            if (cmd.Length > 0)
            {
                string title = text.Substring(cmd.Length + 1);
                for (int i = 0; i < MMStart.SDB.Player.CurrentPlaylist.Count; i++)
                {
                    if (MMStart.SDB.Player.CurrentPlaylist.get_Item(i).Title.Equals(title))
                    {
                        MMStart.SDB.Player.CurrentSongIndex = i;
                    }
                }
            }
        }

        private void HandleJumpBackward(string text)
        {
            string[] matchingCommands = CheckForStartingWith("jump backward", text);
            //Longest command matched is probably the best one
            string cmd = getLongestString(matchingCommands);
            int j = 1;
            try
            {
                j = Convert.ToInt32(text.Substring(cmd.Length + 1));
            }
            catch (Exception e)
            {
            }

            if (j > 0 && MMStart.SDB.Player.CurrentSongIndex - j > 0)
            {
                //Media monkey handles the play and stuff all we have to do is set the index
                MMStart.SDB.Player.CurrentSongIndex -= j;
            }
        }

        private void HandleJumpForward(string text)
        {
            string[] matchingCommands = CheckForStartingWith("jump forward", text);
            //Longest command matched is probably the best one
            string cmd = getLongestString(matchingCommands);
            int j = 1;
            try
            {

                j = Convert.ToInt32(text.Substring(cmd.Length + 1));
            }
            catch (Exception e)
            {
            }

            if (j > 0 && MMStart.SDB.Player.CurrentSongIndex + j < MMStart.SDB.Player.CurrentPlaylist.Count)
            {
                //Media monkey handles the play and stuff all we have to do is set the index
                MMStart.SDB.Player.CurrentSongIndex += j;
            }
        }

        private void HandleJumpToEnd()
        {
            //Media monkey handles the play and stuff all we have to do is set the index
            MMStart.SDB.Player.CurrentSongIndex = MMStart.SDB.Player.CurrentPlaylist.Count - 1;
        }

        private void HandleJumpToStart()
        {
            //Media monkey handles the play and stuff all we have to do is set the index
            MMStart.SDB.Player.CurrentSongIndex = 0;
        }

        private void HandleTogglePause()
        {
            //Pause automatically acts as a toggle
            MMStart.SDB.Player.Pause();
        }


        public void AddGrammar(Grammar g)
        {
            recognitionEngine.LoadGrammar(g);
        }

        public void BuildDefaultGrammars()
        {
            BuildEqualsGrammar("test");
            BuildEqualsGrammar("pause");
            BuildEqualsGrammar("unpause");
            BuildEqualsGrammar("stop");
            BuildEqualsGrammar("next");
            BuildEqualsGrammar("previous");
            BuildEqualsGrammar("play");

            BuildEqualsGrammar("jump to start");
            BuildEqualsGrammar("jump to end");

            BuildEqualsGrammarSS("start listening");
            BuildEqualsGrammarSS("stop listening");


            //You will want to load all the songs, artists, albums, etc... before these run
            BuildPlaySongGrammar();
            BuildPlayArtistGrammar();
            BuildPlayGenreGrammar();
            BuildPlayAlbumGrammar();

            BuildPlayPlaylistGrammar();
            BuildAddSongToNPGrammar();
            BuildAddArtistToNPGrammar();
            BuildAddAlbumToNPGrammar();
            BuildAddGenreToNPGrammar();
            BuildJumpForwardGrammar();
            BuildJumpBackwardGrammar();
            //BuildPlaySongByArtistGrammar(); //Arg doesn't work
        }

        private void BuildAddGenreToNPGrammar()
        {
            var choiceComboGenres = new Choices(genresList.ToArray());

            if (speechCommands.ContainsKey("add genre to now playing"))
            {
                foreach (string cmd in speechCommands["add genre to now playing"])
                {
                    if (cmd.Length > 0)
                    {
                        if (cmd.Contains("{genre}")) // Make sure it's there
                        {
                            //Not too horrible because we know where {song} should be
                            var grammarB = new GrammarBuilder();
                            string[] cmdA = cmd.Split(new string[1] { "{genre}" }, StringSplitOptions.None);
                            //Make sure we don't explode
                            if (cmdA[0].Length > 0) grammarB.Append(cmdA[0]);
                            grammarB.Append(choiceComboGenres);
                            if (cmdA[1].Length > 0) grammarB.Append(cmdA[1]);
                            recognitionEngine.LoadGrammar(new Grammar(grammarB));
                        }
                    }
                }
            }
        }

        private void BuildAddAlbumToNPGrammar()
        {
            var choiceComboAlbums = new Choices(albumsList.ToArray());

            if (speechCommands.ContainsKey("add album to now playing"))
            {
                foreach (string cmd in speechCommands["add album to now playing"])
                {
                    if (cmd.Length > 0)
                    {
                        if (cmd.Contains("{album}")) // Make sure it's there
                        {
                            //Not too horrible because we know where {song} should be
                            var grammarB = new GrammarBuilder();
                            string[] cmdA = cmd.Split(new string[1] { "{album}" }, StringSplitOptions.None);
                            //Make sure we don't explode
                            if (cmdA[0].Length > 0) grammarB.Append(cmdA[0]);
                            grammarB.Append(choiceComboAlbums);
                            if (cmdA[1].Length > 0) grammarB.Append(cmdA[1]);
                            recognitionEngine.LoadGrammar(new Grammar(grammarB));
                        }
                    }
                }
            }
        }

        private void BuildAddArtistToNPGrammar()
        {
            var choiceComboArtists = new Choices(artistsList.ToArray());

            if (speechCommands.ContainsKey("add artist to now playing"))
            {
                foreach (string cmd in speechCommands["add artist to now playing"])
                {
                    if (cmd.Length > 0)
                    {
                        if (cmd.Contains("{artist}")) // Make sure it's there
                        {
                            //Not too horrible because we know where {song} should be
                            var grammarB = new GrammarBuilder();
                            string[] cmdA = cmd.Split(new string[1] { "{artist}" }, StringSplitOptions.None);
                            //Make sure we don't explode
                            if (cmdA[0].Length > 0) grammarB.Append(cmdA[0]);
                            grammarB.Append(choiceComboArtists);
                            if (cmdA[1].Length > 0) grammarB.Append(cmdA[1]);
                            recognitionEngine.LoadGrammar(new Grammar(grammarB));
                        }
                    }
                }
            }
        }

        private void BuildEqualsGrammarSS(string command)
        {
            //Build grammar for the stop listener/start listener
            var choiceCombo = new Choices();
            var grammarB = new GrammarBuilder();
            if (speechCommands.ContainsKey(command) && startStopEnabled)
            {
                foreach (string cmd in speechCommands[command])
                {
                    if (cmd.Length > 0) choiceCombo.Add(cmd);
                }
                grammarB.Append(choiceCombo);
                recognitionEngineSS.LoadGrammar(new Grammar(grammarB));
            }
        }

        public void generateNumbers(int min, int max)
        {
            //Simply generates the numbers for the jump forward/backward commands
            this.numbers = new string[(max - min)];
            for (int i = min; i < max; i++)
            {
                this.numbers[i - min] = i.ToString();
            }
        }

        private void BuildJumpBackwardGrammar()
        {
            var choiceComboCommand = new Choices();
            if (numbers.Length == 0)
            {
                generateNumbers(1, 20);
            }
            var choiceComboNumbers = new Choices(numbers);
            var grammarB = new GrammarBuilder();
            if (speechCommands.ContainsKey("jump backward"))
            {
                foreach (string cmd in speechCommands["jump backward"])
                {
                    if (cmd.Length > 0) choiceComboCommand.Add(cmd);
                }
                //Add the command
                grammarB.Append(choiceComboCommand);
                //Add all the songs afterward
                grammarB.Append(choiceComboNumbers);
                //Load into the recognition engine
                recognitionEngine.LoadGrammar(new Grammar(grammarB));
            }
        }

        private void BuildPlaySongByArtistGrammar()
        {
            //This ones quite tricky actually, you CANNOT have more than 1 {song} and 1 {artist}, sure i could just use a queue...
            var choiceComboArtists = new Choices(artistsList.ToArray());
            var choiceComboSongs = new Choices(songsList.ToArray());
            var grammarB = new GrammarBuilder();

            if (speechCommands.ContainsKey("play song by artist"))
            {
                foreach (string cmd in speechCommands["play song by artist"])
                {
                    if (cmd.Length > 0)
                    {
                        if (cmd.Contains("{song}")) // Make sure it's there
                        {
                            grammarB = new GrammarBuilder();
                            string[] songA = cmd.Split(new string[1] { "{song}" }, StringSplitOptions.None);
                            if (songA[0].Contains("{artist}"))
                            {
                                //Artist is before song
                                string[] artistA = songA[0].Split(new string[1] { "{artist}" }, StringSplitOptions.None);
                                if (artistA[0].Length > 0) grammarB.Append(artistA[0]);
                                grammarB.Append(choiceComboArtists);
                                if (artistA[1].Length > 0) grammarB.Append(artistA[1]);
                                grammarB.Append(choiceComboSongs);
                                if (songA[1].Length > 0) grammarB.Append(songA[1]);
                            }
                            else if (songA[1].Contains("{artist}"))
                            {
                                //Artist is after song
                                if (songA[0].Length > 0) grammarB.Append(songA[0]);
                                grammarB.Append(choiceComboSongs);
                                string[] artistA = songA[1].Split(new string[1] { "{artist}" }, StringSplitOptions.None);
                                if (artistA[0].Length > 0) grammarB.Append(artistA[0]);
                                grammarB.Append(choiceComboArtists);
                                if (artistA[1].Length > 0) grammarB.Append(artistA[1]);
                            }
                            recognitionEngine.LoadGrammar(new Grammar(grammarB));
                        }
                    }
                }
            }
        }

        private void BuildJumpForwardGrammar()
        {
            var choiceComboCommand = new Choices();
            //Make sure we have a set of numbers
            if (numbers.Length < 1) generateNumbers(1, 20);
            var choiceComboNumbers = new Choices(numbers);
            var grammarB = new GrammarBuilder();
            if (speechCommands.ContainsKey("jump forward"))
            {
                foreach (string cmd in speechCommands["jump forward"])
                {
                    if (cmd.Length > 0) choiceComboCommand.Add(cmd);
                }
                //Add the command
                grammarB.Append(choiceComboCommand);
                //Add all the songs afterward
                grammarB.Append(choiceComboNumbers);
                //Load into the recognition engine
                recognitionEngine.LoadGrammar(new Grammar(grammarB));
            }
        }

        private void BuildAddSongToNPGrammar()
        {
            var choiceComboSongs = new Choices(songsList.ToArray());

            if (speechCommands.ContainsKey("add song to now playing"))
            {
                foreach (string cmd in speechCommands["add song to now playing"])
                {
                    if (cmd.Length > 0)
                    {
                        if (cmd.Contains("{song}")) // Make sure it's there
                        {
                            //Not too horrible because we know where {song} should be
                            var grammarB = new GrammarBuilder();
                            string[] cmdA = cmd.Split(new string[1] { "{song}" }, StringSplitOptions.None);
                            //Make sure we don't explode
                            if (cmdA[0].Length > 0) grammarB.Append(cmdA[0]);
                            grammarB.Append(choiceComboSongs);
                            if (cmdA[1].Length > 0) grammarB.Append(cmdA[1]);
                            recognitionEngine.LoadGrammar(new Grammar(grammarB));
                        }
                    }
                }
            }
        }

        private void PlaylistPopup()
        {
            
        }

        private void BuildPlayPlaylistGrammar()
        {
            if (playlistList != null && recognitionEngine != null)
            {
                var choiceComboCommand = new Choices();
                var choiceComboPlaylists = new Choices(playlistList.ToArray());
                var grammarB = new GrammarBuilder();
                if (speechCommands.ContainsKey("play playlist"))
                {
                    foreach (string cmd in speechCommands["play playlist"])
                    {
                        if (cmd.Length > 0) choiceComboCommand.Add(cmd);
                    }
                    //Add the command
                    grammarB.Append(choiceComboCommand);
                    //Add all the songs afterward
                    grammarB.Append(choiceComboPlaylists);
                    //Load into the recognition engine
                    recognitionEngine.LoadGrammar(new Grammar(grammarB));
                }
            }
        }

        public void BuildJumpToSongGrammar()
        {
            if (recognitionEngine != null && MMStart.SDB.Player.CurrentPlaylist.Count>0)
            {
                List<string> playlistSongs = new List<string>();
                for (int i = 0; i < MMStart.SDB.Player.CurrentPlaylist.Count; i++)
                {
                    playlistSongs.Add(MMStart.SDB.Player.CurrentPlaylist.get_Item(i).Title);
                }
                var choiceComboCommand = new Choices();
                
                var choiceComboPlaylist = new Choices(playlistSongs.ToArray());
                var grammarB = new GrammarBuilder();
                if (speechCommands.ContainsKey("jump to song"))
                {
                    foreach (string cmd in speechCommands["jump to song"])
                    {
                        if (cmd.Length > 0) choiceComboCommand.Add(cmd);
                    }
                    //Add the command
                    grammarB.Append(choiceComboCommand);
                    //Add all the songs afterward
                    grammarB.Append(choiceComboPlaylist);
                    jumpToSongGrammar = new Grammar(grammarB);
                    //Load into the recognition engine
                    recognitionEngine.LoadGrammar(jumpToSongGrammar);
                }
            }
        }

        public void StartListening()
        {
            //start to recognize as many times as you want
            recognitionEngine.RecognizeAsync(RecognizeMode.Multiple);
            
            if (recognitionEngineSS.AudioState.Equals(AudioState.Stopped) && startStopEnabled)
                recognitionEngineSS.RecognizeAsync(RecognizeMode.Multiple);
        }

        public void StopListening()
        {
            //Kill recognition right away
            recognitionEngine.RecognizeAsyncCancel();
            //Tell user we arn't listening
            MMStart.activeButton.Visible = false;
            MMStart.activeButton.IconIndex = 30;
            MMStart.activeButton.Caption = "Not listening";
            MMStart.activeButton.Visible = true;
        }

        public void SpeakAsync(string speechText)
        {
            //Just here for external programs to call
            if (speaker != null)
                speaker.SpeakAsync(speechText);
        }

        private void BuildPlayAlbumGrammar()
        {
            var choiceComboCommand = new Choices();
            var choiceComboAlbums = new Choices(albumsList.ToArray());
            var grammarB = new GrammarBuilder();
            if (speechCommands.ContainsKey("play album"))
            {
                foreach (string cmd in speechCommands["play album"])
                {
                    if (cmd.Length > 0) choiceComboCommand.Add(cmd);
                }
                //Add the command
                grammarB.Append(choiceComboCommand);
                //Add all the songs afterward
                grammarB.Append(choiceComboAlbums);
                //Load into the recognition engine
                recognitionEngine.LoadGrammar(new Grammar(grammarB));
            }
        }

        private void BuildPlayGenreGrammar()
        {
            var choiceComboCommand = new Choices();
            var choiceComboGenres = new Choices(genresList.ToArray());
            var grammarB = new GrammarBuilder();
            if (speechCommands.ContainsKey("play genre"))
            {
                foreach (string cmd in speechCommands["play genre"])
                {
                    if (cmd.Length > 0) choiceComboCommand.Add(cmd);
                }

                //Add the command
                grammarB.Append(choiceComboCommand);
                //Add all the songs afterward
                grammarB.Append(choiceComboGenres);
                //Load into the recognition engine
                recognitionEngine.LoadGrammar(new Grammar(grammarB));
            }
        }

        private void BuildPlayArtistGrammar()
        {
            var choiceComboCommand = new Choices();
            var choiceComboArtists = new Choices(artistsList.ToArray());


            var grammarB = new GrammarBuilder();
            if (speechCommands.ContainsKey("play artist"))
            {
                foreach (string cmd in speechCommands["play artist"])
                {
                    if (cmd.Length > 0) choiceComboCommand.Add(cmd);
                }
                //Add the command
                grammarB.Append(choiceComboCommand);
                //Add all the songs afterward
                grammarB.Append(choiceComboArtists);
                //Load into the recognition engine
                recognitionEngine.LoadGrammar(new Grammar(grammarB));
            }
        }

        private void BuildPlaySongGrammar()
        {
            var choiceComboCommand = new Choices();
            var choiceComboSongs = new Choices(songsList.ToArray());
            var grammarB = new GrammarBuilder();
            if (speechCommands.ContainsKey("play song"))
            {
                foreach (string cmd in speechCommands["play song"])
                {
                    if (cmd.Length > 0) choiceComboCommand.Add(cmd);
                }
                //Add the command
                grammarB.Append(choiceComboCommand);
                //Add all the songs afterward
                grammarB.Append(choiceComboSongs);
                //Load into the recognition engine
                recognitionEngine.LoadGrammar(new Grammar(grammarB));
            }
        }

        //Builds grammar for a command that is checked with equals
        private void BuildEqualsGrammar(string command)
        {
            var choiceCombo = new Choices();
            var grammarB = new GrammarBuilder();
            if (speechCommands.ContainsKey(command))
            {
                foreach (string cmd in speechCommands[command])
                {
                    if (cmd.Length > 0) choiceCombo.Add(cmd);
                }
                grammarB.Append(choiceCombo);
                recognitionEngine.LoadGrammar(new Grammar(grammarB));
            }
        }

        private void HandlePlayAlbum(string text)
        {
            string[] matchingCommands = CheckForStartingWith("play album", text);
            //Longest command matched is probably the best one
            string cmd = getLongestString(matchingCommands);


            addSongsToPlaylist(
                (SDBSongIterator)MMStart.SDB.Database.QuerySongs("Album=\"" + text.Substring(cmd.Length + 1) + "\""),
                true, true);
        }

        private void HandlePlayGenre(string text)
        {
            string[] matchingCommands = CheckForStartingWith("play genre", text);
            //Longest command matched is probably the best one
            string cmd = getLongestString(matchingCommands);


            addSongsToPlaylist(
                (SDBSongIterator)MMStart.SDB.Database.QuerySongs("Genre=\"" + text.Substring(cmd.Length + 1) + "\""),
                true, true);
        }

        private void HandlePlayArtist(string text)
        {
            string[] matchingCommands = CheckForStartingWith("play artist", text);
            //Longest command matched is probably the best one
            string cmd = getLongestString(matchingCommands);


            addSongsToPlaylist(
                (SDBSongIterator)MMStart.SDB.Database.QuerySongs("Artist=\"" + text.Substring(cmd.Length + 1) + "\""),
                true, true);
        }

        private void HandlePlaySong(string text)
        {
            string[] matchingCommands = CheckForStartingWith("play song", text);
            //Longest command matched is probably the best one
            string cmd = getLongestString(matchingCommands);


            //Now lets get those songs
            addSongsToPlaylist(
                (SDBSongIterator)
                MMStart.SDB.Database.QuerySongs("SongTitle=\"" + text.Substring(cmd.Length + 1) + "\""), true, true);
        }


        //Checks that the speech equals
        private string[] CheckForEquals(string command, string speechText)
        {
            if (speechCommands.ContainsKey(command))
            {
                var matches = new List<string>();
                foreach (string commandValue in speechCommands[command])
                {
                    if (commandValue.Equals(speechText)) matches.Add(commandValue);
                }
                return matches.ToArray();
            }
            else
            {
                return new string[0];
            }
        }

        //Checks that the speech starts with the command
        private string[] CheckForStartingWith(string command, string speechText)
        {
            if (speechCommands.ContainsKey(command))
            {
                var matches = new List<string>();
                foreach (string commandValue in speechCommands[command])
                {
                    if (speechText.StartsWith(commandValue)) matches.Add(commandValue);
                }
                return matches.ToArray();
            }
            else
            {
                return new string[0];
            }
        }

        //Checks if speech matches the commands pattern 
        private string[] CheckForMatching(string command, string speechText)
        { //Does not work yet
            //Old friend from perl :)

            Regex tagRegex = new Regex("\\{[a-zA-Z]+\\}");
            var matches = new List<string>();
            foreach (string commandValue in speechCommands[command])
            {
                bool match = true;
                int diff = 0;
                MatchCollection mc = tagRegex.Matches(commandValue);
                string[] mcComplement = new string[mc.Count];
                for (int i = 0; i < mc.Count; i++)
                {
                    if (i < mc.Count - 2)
                    {
                        int nextGuy = mc[i + 1].Index;
                        string textBeforeNextGuy = commandValue.Substring(mc[i].Index + mc[i].Length + diff, nextGuy);

                    }
                }
            }
            return matches.ToArray();
        }

        //Adds command to possible commands, Ex: addCommand("play song","play the song ")
        public void AddCommand(string command, string commandText)
        {
            if (commandText.Length > 0)
            {
                if (!commandText[commandText.Length - 1].Equals(' '))
                {
                    commandText += " ";
                }
                if (!speechCommands.ContainsKey(command))
                {
                    speechCommands.Add(command, new List<string>(new string[1] { commandText }));
                }
                else
                {
                    speechCommands[command].Add(commandText);
                }
            }
        }

        //Converts the enum verison into the string version
        public void AddCommand(CommandType cType, string commandText)
        {
            string temp = cType.ToString("F");
            string built = "";
            for (int i = 0; i < temp.Length; i++)
            {
                if ((temp[i]) < 90 && (temp[i]) > 64 && i > 0)
                {
                    //Capital Letter
                    built += " " + temp[i];
                }
                else
                {
                    built += temp[i];
                }
            }
            AddCommand(built.ToLower(), commandText);
        }

        private string getLongestString(string[] stringArray)
        {
            string longest = "";
            //Get the longest command that matched, if there is more than 1
            if (stringArray.Length > 1)
            {
                int max = 0;

                foreach (string command in stringArray)
                {
                    if (command.Length > max)
                    {
                        max = command.Length;
                        longest = command;
                    }
                }
            }
            else
            {
                if (stringArray.Length > 0)
                    longest = stringArray[0];
            }
            return longest;
        }

        private void addSongsToPlaylist(SDBSongIterator songIterator, Boolean autoplay, Boolean clear)
        {
            if (!songIterator.EOF && clear)
            {
                //Reset Playlist
                MMStart.SDB.Player.Stop();
                MMStart.SDB.Player.PlaylistClear();
            }
            else
            {
                //MessageBox.Show("oh Snap " + songIterator.Item.Title);
            }

            List<SDBSongData> songs = new List<SDBSongData>();

            //Add all the selected songs to the playlist
            while (!songIterator.EOF)
            {
               // To allow for dialog box
                songs.Add(songIterator.Item);
                songIterator.Next();
            }

            if(songs.Count>1 && autoAddSongs==false)
            {
                //Make recognition better
                StopListening();
                //ParameterizedThreadStart pickerThread = new ParameterizedThreadStart(RunPicker);
                //new Thread(pickerThread).Start(songs.ToArray());

                songSelection = new SelectionForm(songs.ToArray());

                songSelection.OnSelectionComplete += songSelection_SelectionComplete;
                songSelection.ShowDialog();
                
                
            }
            else
            {
                //make sure there is a song in it
                if(songs.Count>0)
                foreach(SDBSongData song in songs)
                {
                    MMStart.SDB.Player.PlaylistAddTrack(song); 
                }
                BuildJumpToSongGrammar();
            }


            if (autoplay)
            {
                //Start up the playlist
                MMStart.SDB.Player.Play();
            }
        }

        void RunPicker(object songs)
        {
           
        }

        void songSelection_SelectionComplete(SDBSongData[] SelectedSongList)
        {
            StartListening();
            MMStart.activeButton.Visible = false;
            MMStart.activeButton.IconIndex = 51;
            MMStart.activeButton.Caption = "Listening";
            MMStart.activeButton.Visible = true;
            if (SelectedSongList.Length > 0)
            {
                foreach (SDBSongData song in SelectedSongList)
                {
                    MMStart.SDB.Player.PlaylistAddTrack(song);
                }
            }else
            {
                Logger.log("Selected Song List IS not of Any Size"); 
            }
            BuildJumpToSongGrammar();


        }

        //Handlers

        private void HandlePlay()
        {
            MMStart.SDB.Player.Play();
        }

        private void HandlePrevious()
        {
            MMStart.SDB.Player.Previous();
        }

        private void HandleNext()
        {
            MMStart.SDB.Player.Next();
        }

        private void HandleStop()
        {
            MMStart.SDB.Player.Stop();
        }

        private void HandleUnpause()
        {
            if (MMStart.SDB.Player.isPaused) MMStart.SDB.Player.Pause();
        }

        private void HandlePause()
        {
            if (!MMStart.SDB.Player.isPaused) MMStart.SDB.Player.Pause();
        }

        private void HandleTest()
        {
            speaker.SpeakAsync("Testing Successful");
        }

        //Simple add each part
        public void AddGenre(string genreName)
        {
            genresList.Add(genreName);
        }

        public void AddAlbum(string albumName)
        {
            genresList.Add(albumName);
        }

        public void AddSong(string songName)
        {
            genresList.Add(songName);
        }

        public void AddArtist(string artistName)
        {
            genresList.Add(artistName);
        }

        public void UnloadCurrentJumpToSongGrammar()
        {
            if (jumpToSongGrammar != null && recognitionEngine.Grammars.Contains(jumpToSongGrammar))
                recognitionEngine.UnloadGrammar(jumpToSongGrammar);
        }

        public void TerminateAllBonds()
        {
            Logger.log("Terminating all bonds");
            recognitionEngine.SpeechRecognized -= recognitionEngine_SpeechRecognized;
            recognitionEngineSS.SpeechRecognized -= recognitionEngineSS_SpeechRecognized;
            this.recognitionEngineSS.RecognizeAsyncCancel();
            this.recognitionEngineSS.UnloadAllGrammars();
            // this.recognitionEngineSS = null;

            this.recognitionEngine.RecognizeAsyncCancel();
            this.recognitionEngine.UnloadAllGrammars();
            //this.recognitionEngine = null;
            // this.genresList = null;
            this.genresList = new List<string>();
            //this.speaker.Dispose();
            this.artistsList = new List<string>();
            this.songsList = new List<string>();
            this.playlistList = new List<string>();
            this.speechCommands.Clear();
            //this.speechCommands = null;
        }
    }
}